# Replication code for ``Constituents’ Responses to LGB Representatives in Congress''
# Political Behavior
# Phil Jones, University of Delaware, pejones@udel.edu
# March 20, 2026

library(here)
library(haven)
library(tidyverse)
library(survey)
library(lme4)
library(texreg)
library(ggeffects)

data <- readRDS(here("data", "data.RData"))
set.seed(2024)


###############################################################################
# TABLES 1--3: MULTI-LEVEL REGRESSION MODELS
###############################################################################

# Table 1, model (a): Approve of MC's job
m1a <- lmer(mc_approval~self_lgbt*mc_lgb + 
             mc_party_congruence+race+women+educ+rel_imp+age+interest+income+
             mc_dem+mc_black+mc_asian+mc_hispanic+mc_woman+mc_tenure_c+cd_dempres_c+cd_compres_c +
             (1|mc_name_district_year), data)

# Table 1, model (b): Approve of MC's job with interaction for party congruence
m1b <- lmer(mc_approval~self_lgbt*mc_lgb*mc_party_congruence + 
              race+women+educ+rel_imp+age+interest+income+
              mc_dem+mc_black+mc_asian+mc_hispanic+mc_woman+mc_tenure_c+cd_dempres_c+cd_compres_c +
              (1|mc_name_district_year), data)

# Table 1, model (c): Approve of Congress
m1c <- lmer(cong_approval~self_lgbt*mc_lgb +
             mc_party_congruence+race+women+educ+rel_imp+age+interest+income+
             mc_dem+mc_black+mc_asian+mc_hispanic+mc_woman+mc_tenure_c+cd_dempres_c+cd_compres_c +
             (1|mc_name_district_year), data)



# Table 2, model (a): Know MC's party
m2a <- glmer(know_mc_party~self_lgbt*mc_lgb +
              mc_party_congruence+race+women+educ+rel_imp+age+interest+income+
              mc_dem+mc_black+mc_asian+mc_hispanic+mc_woman+mc_tenure_c+cd_dempres_c+cd_compres_c +
              (1|mc_name_district_year), data, family="binomial")

# Table 2, model (b): Can rate MC's job
m2b <- glmer(rate_mc_approval~self_lgbt*mc_lgb +
              mc_party_congruence+race+women+educ+rel_imp+age+interest+income+
              mc_dem+mc_black+mc_asian+mc_hispanic+mc_woman+mc_tenure_c+cd_dempres_c+cd_compres_c +
              (1|mc_name_district_year), data, family="binomial")

# Table 2, model (c): Can rate MC's ideology
m2c <- glmer(rate_mc_ideology~self_lgbt*mc_lgb +
              mc_party_congruence+race+women+educ+rel_imp+age+interest+income+
              mc_dem+mc_black+mc_asian+mc_hispanic+mc_woman+mc_tenure_c+cd_dempres_c+cd_compres_c +
              (1|mc_name_district_year), data, family="binomial")



# Table 3: Perceptions of MC's ideology
m3 <- lmer(mc_ideology~self_lgbt*mc_lgb +
             mc_party_congruence+race+women+educ+rel_imp+age+interest+income+
             mc_dem+mc_black+mc_asian+mc_hispanic+mc_woman+mc_tenure_c+cd_dempres_c+cd_compres_c + dnom_lastcongress_c +
             (1|mc_name_district_year), data)



###############################################################################
# FIGURE 1
###############################################################################

set.seed(2024)

# create default values for controls when simulating (others will be set to population  means)
sim.cond <- c(mc_dem=1, mc_black=0, women=1, mc_asian=0, mc_hispanic=0, mc_woman=0, mc_tenure_c=0, cd_dempres_c=0, cd_compres_c=0, dnom_last_congress=.06)

# Simulate first difference of being represented by LGB MC on...

# m1a: Approval of MC's job, by constituent sexuality
m1a_fd <- as.data.frame(test_predictions(m1a, terms = c("mc_lgb", "self_lgbt"), condition=sim.cond, test=NULL), terms_to_colnames=T)
m1a_fd$self_lgbt <- as.factor(m1a_fd$self_lgbt)

# m1b: Approval of MC's job, by constituent sexuality amd partisan congruence
m1b_fd <- as.data.frame(test_predictions(m1b, terms = c("mc_lgb", "self_lgbt", "mc_party_congruence"), condition=sim.cond, test=NULL), terms_to_colnames=T)
m1b_fd$self_lgbt <- as.factor(m1b_fd$self_lgbt)
m1b_fd$mc_party_congruence <- forcats::fct_relevel(m1b_fd$mc_party_congruence, "Other party", "Independent", "Same party")

# m1c: Approval of Congress, by constituent sexuality
m1c_fd <- as.data.frame(test_predictions(m1c, terms = c("mc_lgb", "self_lgbt"), condition=sim.cond, test=NULL), terms_to_colnames=T)
m1c_fd$self_lgbt <- as.factor(m1c_fd$self_lgbt)


# ggplot theme defaults
theme_2024 <- function(){
  theme(axis.title=element_text(size=11),
        plot.title=element_text(size=12, hjust=.5, face="bold"),
        axis.ticks=element_line(linewidth=.5, color="gray55"),
        axis.text.x=element_text(size=9, color="black"),
        axis.text.y=element_text(size=11, color="black"))
}


# Figure 1(a): Difference in approval of LGB MCs
g1 <- ggplot(m1a_fd, aes(x=self_lgbt, y=Slope, ymin=conf.low, ymax=conf.high)) +
  geom_hline(yintercept=0, color="gray55", linetype=1, linewidth=.5) +
  geom_errorbar(width=.1)+
  geom_point(size=3.5, shape=21, fill="black") +
  scale_y_continuous(limits=c(-.2,.2), breaks=c(-.2, -.1, 0, .1, .2)) + 
  scale_x_discrete(labels=c("Straight/cis\nconstituents", "LGBT\nconstituents")) +
  ylab("First difference from those represented by a straight MC") + xlab("") +
  ggtitle("(a) Difference in approval of LGB MCs") +
  coord_flip() + theme_2024()
g1
ggsave("fds_1a.pdf", width=5.5, height=3)


# Figure 1(b): Difference in approval of LGB MCs, by partisan congruence with MC
g1b <- ggplot(m1b_fd, aes(y=self_lgbt, x=Slope, xmin=conf.low, xmax=conf.high, color=mc_party_congruence, fill=mc_party_congruence, shape=mc_party_congruence)) +
  geom_vline(xintercept=0, color="gray55", linetype=1, linewidth=.5) +
  geom_errorbar(width=.2, position = position_dodge(width=.5))+
  geom_point(size=3.5, position = position_dodge(width=.5)) +
  scale_x_continuous(limits=c(-.2,.211), breaks=c(-.2, -.1, 0, .1, .2, .3)) + 
  scale_y_discrete(labels=c("Straight/cis\nconstituents", "LGBT\nconstituents")) +
  xlab("First difference from those represented by a straight MC") + ylab("") +
  ggtitle("(b) Difference in approval of LGB MCs,\nby partisan congruence with MC") +
  theme_2024()
g1b + scale_fill_manual(
  values = c("Same party" = "#009E73", "Independent" = "black", "Other party"="#D55E00"),
  breaks = c("Same party", "Independent", "Other party"),
  labels = c("Same party   ", "Independent   ", "Other party   ")) +
  scale_color_manual(
    values = c("Same party" = "#009E73", "Independent" = "black", "Other party"="#D55E00"),
    breaks = c("Same party", "Independent", "Other party"),
    labels = c("Same party   ", "Independent   ", "Other party   ")) +
  scale_shape_manual(
    values = c("Same party" = 21, "Independent" = 22, "Other party"=23),
    breaks = c("Same party", "Independent", "Other party"),
    labels = c("Same party   ", "Independent   ", "Other party   ")) +
  labs(fill="", color="", shape="") +
  theme(legend.position = "top", legend.text=element_text(size=10))
ggsave("fds_1b.pdf", width=5.5, height=3.65)


# Figure 1(c): Difference in approval of Congress
g1c <- ggplot(m1c_fd, aes(x=self_lgbt, y=Slope, ymin=conf.low, ymax=conf.high)) +
  geom_hline(yintercept=0, color="gray55", linetype=1, linewidth=.5) +
  geom_errorbar(width=.1)+
  geom_point(size=3.5, shape=21, fill="black") +
  scale_y_continuous(limits=c(-.2,.2), breaks=c(-.2, -.1, 0, .1, .2)) + 
  scale_x_discrete(labels=c("Straight/cis\nconstituents", "LGBT\nconstituents")) +
  ylab("First difference from those represented by a straight MC") + xlab("") +
  ggtitle("(c) Difference in approval of Congress") +
  coord_flip() + theme_2024()
g1c
ggsave("fds_1c.pdf", width=5.5, height=3)


# Test to see if first differences for straight and LGBT constituents differ significantly
test_predictions(m1a, terms = c("self_lgbt", "mc_lgb"), condition=sim.cond)
test_predictions(m1b, terms = c("self_lgbt", "mc_lgb"), condition=sim.cond)
test_predictions(m1c, terms = c("self_lgbt", "mc_lgb"), condition=sim.cond)



###############################################################################
# FIGURE 2
###############################################################################

# Simulate first difference of being represented by LGB MC on...

# m2a: Knowledge of MC's party, by constituent sexuality
m2a_fd <- as.data.frame(test_predictions(m2a, terms = c("mc_lgb", "self_lgbt"), condition=sim.cond, test=NULL), terms_to_colnames=T)
m2a_fd$self_lgbt <- as_factor(m2a_fd$self_lgbt)

# m2b: Ability to rate MC's job, by constituent sexuality
m2b_fd <- as.data.frame(test_predictions(m2b, terms = c("mc_lgb", "self_lgbt"), condition=sim.cond, test=NULL), terms_to_colnames=T)
m2b_fd$self_lgbt <- as_factor(m2b_fd$self_lgbt)

# m2c: Ability to rate MC's ideology, by constituent sexuality
m2c_fd <- as.data.frame(test_predictions(m2c, terms = c("mc_lgb", "self_lgbt"), condition=sim.cond, test=NULL), terms_to_colnames=T)
m2c_fd$self_lgbt <- as_factor(m2c_fd$self_lgbt)


# Figure 2(a): Difference in knowledge of LGB MCs' party
g2a <- ggplot(m2a_fd, aes(x=self_lgbt, y=Slope, ymin=conf.low, ymax=conf.high)) +
  geom_hline(yintercept=0, color="gray55", linetype=1, linewidth=.5) +
  geom_errorbar(width=.1)+
  geom_point(size=3.5, shape=21, fill="black") +
  scale_y_continuous(limits=c(-.2,.2), breaks=c(-.2, -.1, 0, .1, .2)) + 
  scale_x_discrete(labels=c("Straight/cis\nconstituents", "LGBT\nconstituents")) +
  ylab("First difference from those represented by a straight MC") + xlab("") +
  ggtitle("(a) Difference in knowledge of LGB MCs' party") +
  coord_flip() + theme_2024()
g2a
ggsave("fds_2a.pdf", width=5.5, height=3)


# Figure 2(b): Difference in ability to rate LGB MCs' job
g2b <- ggplot(m2b_fd, aes(x=self_lgbt, y=Slope, ymin=conf.low, ymax=conf.high)) +
  geom_hline(yintercept=0, color="gray55", linetype=1, linewidth=.5) +
  geom_errorbar(width=.1)+
  geom_point(size=3.5, shape=21, fill="black") +
  scale_y_continuous(limits=c(-.2,.2), breaks=c(-.2, -.1, 0, .1, .2)) + 
  scale_x_discrete(labels=c("Straight/cis\nconstituents", "LGBT\nconstituents")) +
  ylab("First difference from those represented by a straight MC") + xlab("") +
  ggtitle("(b) Difference in ability to rate LGB MCs' job") +
  coord_flip() + theme_2024()
g2b
ggsave("fds_2b.pdf", width=5.5, height=3)


# Figure 2(c): Difference in ability to rate LGB MCs' ideology
g2c <- ggplot(m2c_fd, aes(x=self_lgbt, y=Slope, ymin=conf.low, ymax=conf.high)) +
  geom_hline(yintercept=0, color="gray55", linetype=1, linewidth=.5) +
  geom_errorbar(width=.1)+
  geom_point(size=3.5, shape=21, fill="black") +
  scale_y_continuous(limits=c(-.2,.2), breaks=c(-.2, -.1, 0, .1, .2)) + 
  scale_x_discrete(labels=c("Straight/cis\nconstituents", "LGBT\nconstituents")) +
  ylab("First difference from those represented by a straight MC") + xlab("") +
  ggtitle("(c) Difference in ability to rate LGB MCs' ideology") +
  coord_flip() + theme_2024()
g2c
ggsave("fds_2c.pdf", width=5.5, height=3)


# Test to see if first differences for straight and LGBT constituents differ significantly
test_predictions(m2a, terms = c("self_lgbt", "mc_lgb"), condition=sim.cond)
test_predictions(m2b, terms = c("self_lgbt", "mc_lgb"), condition=sim.cond)
test_predictions(m2c, terms = c("self_lgbt", "mc_lgb"), condition=sim.cond)



###############################################################################
# FIGURE 3
###############################################################################

# Simulate first difference of being represented by LGB MC on perceived ideology, by constituent sexuality
m3_fd <- as.data.frame(test_predictions(m3, terms = c("mc_lgb", "self_lgbt"), condition=sim.cond, test=NULL), terms_to_colnames=T)
m3_fd$self_lgbt <- as_factor(m3_fd$self_lgbt)

# Figure 3: Difference in perception of LGB MCs' ideology
g3 <- ggplot(m3_fd, aes(x=self_lgbt, y=Slope, ymin=conf.low, ymax=conf.high)) +
  geom_hline(yintercept=0, color="gray55", linetype=1, linewidth=.5) +
  geom_errorbar(width=.1)+
  geom_point(size=3.5, shape=21, fill="black") +
  scale_y_continuous(limits=c(-.2,.2), breaks=c(-.2, -.1, 0, .1, .2)) + 
  scale_x_discrete(labels=c("Straight/cis\nconstituents", "LGBT\nconstituents")) +
  ylab("First difference from those represented by a straight MC") + xlab("") +
  coord_flip() + theme_2024()
g3
ggsave("fds_3.pdf", width=5.5, height=3)


# Test to see if first differences for straight and LGBT constituents differ significantly
test_predictions(m3, terms = c("self_lgbt", "mc_lgb"), condition=sim.cond)


